//===--- PILLocation.cpp - Location information for PIL nodes -------------===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See https://swift.org/LICENSE.txt for license information
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
//===----------------------------------------------------------------------===//

#include "polarphp/pil/lang/PILLocation.h"
#include "polarphp/ast/Decl.h"
#include "polarphp/ast/Expr.h"
#include "polarphp/ast/Pattern.h"
#include "polarphp/ast/Stmt.h"
#include "polarphp/basic/SourceMgr.h"
#include "llvm/Support/raw_ostream.h"

using namespace polar;

// 64-bit is 24 bytes, 32-bit is 20 bytes.
static_assert(sizeof(PILLocation) == sizeof(void *) + 4*sizeof(unsigned),
              "PILLocation must stay small");

SourceLoc PILLocation::getSourceLoc() const {
   if (isPILFile())
      return Loc.PILFileLoc;

   // Don't crash if the location is a DebugLoc.
   // TODO: this is a workaround until rdar://problem/25225083 is implemented.
   if (isDebugInfoLoc())
      return SourceLoc();

   return getSourceLoc(Loc.AstNode.Primary);
}

SourceLoc PILLocation::getSourceLoc(AstNodeTy N) const {
   if (N.isNull())
      return SourceLoc();

   if (alwaysPointsToStart() ||
       alwaysPointsToEnd() ||
       is<CleanupLocation>() ||
       is<ImplicitReturnLocation>())
      return getEndSourceLoc(N);

   // Use the start location for the ReturnKind.
   if (is<ReturnLocation>())
      return getStartSourceLoc(N);

   if (auto *decl = N.dyn_cast<Decl*>())
      return decl->getLoc();
   if (auto *expr = N.dyn_cast<Expr*>())
      return expr->getLoc();
   if (auto *stmt = N.dyn_cast<Stmt*>())
      return stmt->getStartLoc();
   if (auto *patt = N.dyn_cast<Pattern*>())
      return patt->getStartLoc();
   llvm_unreachable("impossible PILLocation");
}

SourceLoc PILLocation::getDebugSourceLoc() const {
   assert(!isDebugInfoLoc());

   if (isPILFile())
      return Loc.PILFileLoc;

   if (auto *expr = Loc.AstNode.Primary.dyn_cast<Expr*>()) {
      // Code that has an autoclosure as location should not show up in
      // the line table (rdar://problem/14627460). Note also that the
      // closure function still has a valid DW_AT_decl_line.  Depending
      // on how we decide to resolve rdar://problem/14627460, we may
      // want to use the regular getLoc instead and rather use the
      // column info.
      if (isa<AutoClosureExpr>(expr))
         return SourceLoc();
   }

   if (Loc.AstNode.ForDebugger)
      return getSourceLoc(Loc.AstNode.ForDebugger);

   return getSourceLoc(Loc.AstNode.Primary);
}

SourceLoc PILLocation::getStartSourceLoc() const {
   if (isAutoGenerated())
      return SourceLoc();
   if (isPILFile())
      return Loc.PILFileLoc;
   return getStartSourceLoc(Loc.AstNode.Primary);
}

SourceLoc PILLocation::getStartSourceLoc(AstNodeTy N) const {
   if (auto *decl = N.dyn_cast<Decl*>())
      return decl->getStartLoc();
   if (auto *expr = N.dyn_cast<Expr*>())
      return expr->getStartLoc();
   if (auto *stmt = N.dyn_cast<Stmt*>())
      return stmt->getStartLoc();
   if (auto *patt = N.dyn_cast<Pattern*>())
      return patt->getStartLoc();
   llvm_unreachable("impossible PILLocation");
}

SourceLoc PILLocation::getEndSourceLoc() const {
   if (isAutoGenerated())
      return SourceLoc();
   if (isPILFile())
      return Loc.PILFileLoc;
   return getEndSourceLoc(Loc.AstNode.Primary);
}

SourceLoc PILLocation::getEndSourceLoc(AstNodeTy N) const {
   if (auto decl = N.dyn_cast<Decl*>())
      return decl->getEndLoc();
   if (auto expr = N.dyn_cast<Expr*>())
      return expr->getEndLoc();
   if (auto stmt = N.dyn_cast<Stmt*>())
      return stmt->getEndLoc();
   if (auto patt = N.dyn_cast<Pattern*>())
      return patt->getEndLoc();
   llvm_unreachable("impossible PILLocation");
}

DeclContext *PILLocation::getAsDeclContext() const {
   if (!isAstNode())
      return nullptr;
   if (auto *D = getAsAstNode<Decl>())
      return D->getInnermostDeclContext();
   if (auto *E = getAsAstNode<Expr>())
      if (auto *DC = dyn_cast<AbstractClosureExpr>(E))
         return DC;
   return nullptr;
}

PILLocation::DebugLoc PILLocation::decode(SourceLoc Loc,
                                          const SourceManager &SM) {
   DebugLoc DL;
   if (Loc.isValid()) {
      DL.Filename = SM.getDisplayNameForLoc(Loc);
      std::tie(DL.Line, DL.Column) = SM.getLineAndColumn(Loc);
   }
   return DL;
}

void PILLocation::dump(const SourceManager &SM) const {
   if (auto D = getAsAstNode<Decl>())
      llvm::errs() << Decl::getKindName(D->getKind()) << "Decl @ ";
   if (auto E = getAsAstNode<Expr>())
      llvm::errs() << Expr::getKindName(E->getKind()) << "Expr @ ";
   if (auto S = getAsAstNode<Stmt>())
      llvm::errs() << Stmt::getKindName(S->getKind()) << "Stmt @ ";
   if (auto P = getAsAstNode<Pattern>())
      llvm::errs() << Pattern::getKindName(P->getKind()) << "Pattern @ ";

   print(llvm::errs(), SM);

   if (isAutoGenerated())     llvm::errs() << ":auto";
   if (alwaysPointsToStart()) llvm::errs() << ":start";
   if (alwaysPointsToEnd())   llvm::errs() << ":end";
   if (isInTopLevel())        llvm::errs() << ":toplevel";
   if (isInPrologue())        llvm::errs() << ":prologue";
   if (isPILFile())           llvm::errs() << ":sil";
   if (hasDebugLoc()) {
      llvm::errs() << ":debug[";
      getDebugSourceLoc().print(llvm::errs(), SM);
      llvm::errs() << "]\n";
   }
}

void PILLocation::print(raw_ostream &OS, const SourceManager &SM) const {
   if (isNull())
      OS << "<no loc>";
   getSourceLoc().print(OS, SM);
}

InlinedLocation InlinedLocation::getInlinedLocation(PILLocation L) {
   if (Expr *E = L.getAsAstNode<Expr>())
      return InlinedLocation(E, L.getSpecialFlags());
   if (Stmt *S = L.getAsAstNode<Stmt>())
      return InlinedLocation(S, L.getSpecialFlags());
   if (Pattern *P = L.getAsAstNode<Pattern>())
      return InlinedLocation(P, L.getSpecialFlags());
   if (Decl *D = L.getAsAstNode<Decl>())
      return InlinedLocation(D, L.getSpecialFlags());

   if (L.isPILFile())
      return InlinedLocation(L.Loc.PILFileLoc, L.getSpecialFlags());

   if (L.isInTopLevel())
      return InlinedLocation::getModuleLocation(L.getSpecialFlags());

   if (L.isAutoGenerated()) {
      InlinedLocation IL;
      IL.markAutoGenerated();
      return IL;
   }
   llvm_unreachable("Cannot construct Inlined loc from the given location.");
}

MandatoryInlinedLocation
MandatoryInlinedLocation::getMandatoryInlinedLocation(PILLocation L) {
   if (Expr *E = L.getAsAstNode<Expr>())
      return MandatoryInlinedLocation(E, L.getSpecialFlags());
   if (Stmt *S = L.getAsAstNode<Stmt>())
      return MandatoryInlinedLocation(S, L.getSpecialFlags());
   if (Pattern *P = L.getAsAstNode<Pattern>())
      return MandatoryInlinedLocation(P, L.getSpecialFlags());
   if (Decl *D = L.getAsAstNode<Decl>())
      return MandatoryInlinedLocation(D, L.getSpecialFlags());

   if (L.isPILFile())
      return MandatoryInlinedLocation(L.Loc.PILFileLoc, L.getSpecialFlags());

   if (L.isInTopLevel())
      return MandatoryInlinedLocation::getModuleLocation(L.getSpecialFlags());

   if (L.isDebugInfoLoc())
      return MandatoryInlinedLocation(L.getDebugInfoLoc(), L.getSpecialFlags());

   llvm_unreachable("Cannot construct Inlined loc from the given location.");
}

MandatoryInlinedLocation MandatoryInlinedLocation::getAutoGeneratedLocation() {
   return getMandatoryInlinedLocation(
      RegularLocation::getAutoGeneratedLocation());
}

CleanupLocation CleanupLocation::get(PILLocation L) {
   if (Expr *E = L.getAsAstNode<Expr>())
      return CleanupLocation(E, L.getSpecialFlags());
   if (Stmt *S = L.getAsAstNode<Stmt>())
      return CleanupLocation(S, L.getSpecialFlags());
   if (Pattern *P = L.getAsAstNode<Pattern>())
      return CleanupLocation(P, L.getSpecialFlags());
   if (Decl *D = L.getAsAstNode<Decl>())
      return CleanupLocation(D, L.getSpecialFlags());
   if (L.isNull())
      return CleanupLocation();
   if (L.isPILFile())
      return CleanupLocation();
   if (L.isDebugInfoLoc() && L.isAutoGenerated())
      return CleanupLocation();
   llvm_unreachable("Cannot construct Cleanup loc from the "
                    "given location.");
}

ReturnLocation::ReturnLocation(ReturnStmt *RS) : PILLocation(RS, ReturnKind) {}

ReturnLocation::ReturnLocation(BraceStmt *BS) : PILLocation(BS, ReturnKind) {}

ReturnStmt *ReturnLocation::get() {
   return castToAstNode<ReturnStmt>();
}

ImplicitReturnLocation::ImplicitReturnLocation(AbstractClosureExpr *E)
   : PILLocation(E, ImplicitReturnKind) { }

ImplicitReturnLocation::ImplicitReturnLocation(ReturnStmt *S)
   : PILLocation(S, ImplicitReturnKind) { }

ImplicitReturnLocation::ImplicitReturnLocation(AbstractFunctionDecl *AFD)
   : PILLocation(AFD, ImplicitReturnKind) { }

PILLocation ImplicitReturnLocation::getImplicitReturnLoc(PILLocation L) {
   assert(L.isAstNode<Expr>() ||
          L.isAstNode<ValueDecl>() ||
          L.isAstNode<PatternBindingDecl>() ||
          (L.isNull() && L.isInTopLevel()));
   L.setLocationKind(ImplicitReturnKind);
   return L;
}

AbstractClosureExpr *ImplicitReturnLocation::get() {
   return castToAstNode<AbstractClosureExpr>();
}
